#include "../include/Utils.hpp"

void Logger::_Critical(const std::string &tar){
    Critical(tar);
}
void Logger::_Error(const std::string &tar){
    Error(tar);
}
void Logger::_Warning(const std::string &tar){
    Warning(tar);
}
void Logger::_Info(const std::string &tar){
    Info(tar);
}
void Logger::_Debug(const std::string &tar){
    Debug(tar);
}

namespace NoBody{
    Logger logger;
}

LogEnd logEnd;

template <>
_Logger & operator << (_Logger &os, const LogEnd &) {
    if(os.disabled) return os;
    os.call();
    os.clear();
    return os;
}

_Logger dD(bind(&Logger::_Debug, &NoBody::logger, std::placeholders::_1)), 
    iI(bind(&Logger::_Info, &NoBody::logger, std::placeholders::_1)), 
    wW(bind(&Logger::_Warning, &NoBody::logger, std::placeholders::_1)), 
    eE(bind(&Logger::_Error, &NoBody::logger, std::placeholders::_1)), 
    cC(bind(&Logger::_Critical, &NoBody::logger, std::placeholders::_1));

std::string to_string(double tar, int width, int precision) {
	std::stringstream ss;
	if (width >= 0) {
		if (precision >= 0) ss << std::right << std::setw(width) << std::fixed << 
			std::setprecision(precision) << tar;
		else ss << std::right << std::setw(width) << tar;
	} else ss << tar;
	return ss.str();
}

using ::to_string;

/**
 * @brief 用于初始化Logger的，简而言之就是五个调试器就会同时输出到文件和屏幕，如果不初始化就不会输出文件
 */
void Logger::init(){
    std::string targetFile = getTime() + ".log";
    lger.open(targetFile);
    iI << targetFile << (lger.is_open() ? " Opened." : "Not Opened!") << logEnd;
}

std::string Logger::getTime() {
	std::time_t curTime = std::time(NULL);
    struct tm *info = localtime(&curTime);
	strftime(s, 54, "%Y-%m-%d*%H:%M:%S", info);
	return s;
}

template <typename T>
inline void Logger::sendLog(const std::string& logLevel, const T& tar, const std::string& p, std::true_type) {
    std::stringstream ss;
	ss << logLevel << getTime() << " :";
    if(!p.empty()) ss << p << " : ";
    ss << tar;
    const std::string &tmp = ss.str();
    std::cerr << tmp << std::endl;
    lger << tmp << std::endl;
}

template <typename T>
inline void Logger::sendLog(const std::string& logLevel, const T& tar, const std::string& p, std::false_type) {
    std::stringstream ss;
	ss << logLevel << getTime() << " :";
    if(!p.empty()) ss << p << " : ";
    ss << tar;
    lger << ss.str() << std::endl;
}
template <typename T>
inline void Logger::Critical(const T& tar, const std::string& p) {
	sendLog("[Critical]: ", tar, p, std::true_type());
}
template <typename T>
inline void Logger::Error(const T& tar, const std::string& p) {
	sendLog("[Error]: ", tar, p, std::true_type());
}
template <typename T>
inline void Logger::Warning(const T& tar, const std::string& p) {
	sendLog("[Warning]: ", tar, p, std::true_type());
}
template <typename T>
inline void Logger::Info(const T& tar, const std::string& p) {
#ifdef _DEBUG
	sendLog("[Info]: ", tar, p, std::true_type());
#else
	sendLog("[Info]: ", tar, p, std::false_type());
#endif
}
template <typename T>
inline void Logger::Debug(const T& tar, const std::string& p) {
#ifdef _DEBUG
	sendLog("[Debug]: ", tar, p, std::true_type());
#else
	sendLog("[Debug]: ", tar, p, std::false_type());
#endif
}

inline unsigned _Random::rand() {
	return Generator();
}

inline unsigned _Random::rand_64() {
	return Generator_64();
}

inline unsigned _Random::rand(const unsigned& up) {
	return rand() % up;
}

inline unsigned long long _Random::rand_64(const unsigned long long &up){
    return rand_64() % up;
}

inline unsigned _Random::randInt(const unsigned &up) { 
	return rand() % up + 1;
}

inline unsigned long long _Random::randInt_64(const unsigned long long &up) { 
	return rand_64() % up + 1;
}

unsigned _Random::randInt(const unsigned &down, const unsigned &up) { 
	return rand() % (up - down + 1) + down;
}

unsigned long long _Random::randInt_64(const unsigned long long &down, const unsigned long long &up) { 
	return rand_64() % (up - down + 1) + down;
}

double _Random::randDouble() { 
	union {
		double d;
		unsigned long long u;
	}ret;
	ret.u = (Generator_64() >> 12) | 0x3FF0000000000000ULL;
	return ret.d - 1.0;
}

double _Random::randDouble(const double& down, const double& up) {
	return randDouble() * (up - down) + down;
}

unsigned _Random::randBit() {
	return rand() & 1;
}

template <typename Iterator_Type>
void _Random::randomShuffle(Iterator_Type __first, Iterator_Type __last) {
	random_shuffle(__first, __last, this -> rand_64);
}

template <typename Iterator_Type>
inline Iterator_Type _Random::randomPicker(Iterator_Type __first, Iterator_Type __last){
    return __first + this -> rand_64(__last - __first);
}

_Random Random;